home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2003 November A / PCWK1103A.iso / Adobe After Effects 6.0 tryout / MM5.Cab / F2476_commandLineRenderer.jsx.304FA6F7_2783_11D4_8520_00C04F602FD3 next >
Text File  |  2003-07-18  |  31KB  |  880 lines

  1.  // Command line renderer for After Effects.
  2.  
  3. // This function constructs an AECommandLineRenderer.
  4. // One and only one of these will be created to perform rendering tasks
  5. // at the end of this file.
  6. //
  7. // The constructor has 3 sections:
  8. // [1] define all the variable-type attributes used by this class
  9. // [2] define all the functions used by this class
  10. // [3] assign all the functions to be method-type attributes.
  11. function AECommandLineRenderer() {
  12.  
  13.     // [1] define all the variable-type attributes used by this class
  14.     //
  15.     // Input before parsing
  16.     //
  17.     this.inArgs     = null;
  18.     //
  19.     // Input after parsing
  20.     //
  21.     this.in_project_path   = null;
  22.     this.in_comp_name      = null;
  23.     this.in_RStemplate     = null;
  24.     this.in_OMtemplate     = null;
  25.     this.in_output_path    = null;
  26.     this.in_logfile_path   = null;
  27.     this.in_start_frame    = null;
  28.     this.in_end_frame      = null;
  29.     this.in_image_cache_percent   = null;
  30.     this.in_max_mem_percent       = null;
  31.     this.in_verbose_flag   = null;
  32.     this.in_close_flag     = null;
  33.     this.in_sound_flag     = null;
  34.     this.in_port_address   = null;
  35.     //
  36.     // Exit codes:
  37.     //
  38.     this.EXIT_OK                          = 0;
  39.     this.EXIT_FAILURE_CODE_FROM_APP       = 1;
  40.     this.EXIT_SHOW_USAGE                  = 2;
  41.     this.EXIT_SYNTAX_ERROR                = 3;
  42.     this.EXIT_SYNTAX_ERROR_USER_LOG       = 4;
  43.     this.EXIT_OTHER_SCRIPTING_ERROR       = 5;
  44.     this.EXIT_OTHER_SCRIPTING_ERROR_USER_LOG  = 6;
  45.     this.EXIT_AERENDER_RUNTIME              = 7;
  46.     this.EXIT_AERENDER_RUNTIME_USER_LOG     = 8;
  47.     this.EXIT_AE_RUNTIME                    = 9;
  48.     this.EXIT_AE_RUNTIME_USER_LOG           = 10;
  49.     this.EXIT_CANNOT_OPEN_SOCKET            = 11;
  50.     this.EXIT_CODE_NO_LONGER_IN_USE         = 12;
  51.     //
  52.     // Exit code message prefixes:
  53.     //
  54.     this.EXIT_MSG_PREFIX = new Array(
  55.         "",                    // EXIT_OK
  56.         "ERROR: ",            // EXIT_FAILURE_CODE_FROM_APP
  57.         "USAGE: ",            // EXIT_SHOW_USAGE
  58.         "SYNTAX ERROR: ",    // EXIT_SYNTAX_ERROR
  59.         "SYNTAX ERROR: ",    // EXIT_SYNTAX_ERROR_USER_LOG
  60.         "ERROR: ",            // EXIT_OTHER_SCRIPTING_ERROR
  61.         "ERROR: ",            // EXIT_OTHER_SCRIPTING_ERROR_USER_LOG
  62.         "ERROR: ",            // EXIT_AERENDER_ERROR
  63.         "ERROR: ",            // EXIT_AERENDER_ERROR_USER_LOG
  64.         "ERROR: ",            // EXIT_AE_RUNTIME
  65.         "ERROR: ",            // EXIT_AE_RUNTIME_USER_LOG
  66.         "ERROR: ",            // EXIT_CANNOT_OPEN_SOCKET
  67.         "",                    // EXIT_CODE_NO_LONGER_IN_USE
  68.         );
  69.     //
  70.     // Messages:
  71.     //
  72.     this.MSG_NONE                        = "";
  73.     this.MSG_NOT_HANDLED_HERE           = "reported by another script or AE runtime.";
  74.     this.MSG_SHOW_USAGE                 = "";
  75.     this.MSG_TRIED_TO_PARSE_UNDEFINED   = "aerender tried to parse an undefined argument.";
  76.     this.MSG_UNDEFINED_VALUE_FOR_FLAG   = "no value given for flag: "; 
  77.     this.MSG_BAD_FLAG                   = "Illegal argument flag: ";
  78.     this.MSG_NO_PROJECT                 = "No project provided and no project open.";
  79.     this.MSG_BAD_VERBOSE_FLAG           = "Bad value for -verbose.";
  80.     this.MSG_BAD_CLOSE_FLAG             = "Bad value for -close.";
  81.     this.MSG_BAD_SOUND_FLAG             = "Bad value for -sound.";
  82.     this.MSG_COMP_NOT_FOUND             = "No comp was found with the given name.";
  83.     this.MSG_AE_RUNTIME                 = "Runtime error in After Effects.";
  84.     this.MSG_ADDING_TO_RQ               = "PROGRESS: Adding specified comp to Render Queue";
  85.     this.MSG_NEEDS_OUTPUT               = "Specified render queue item needs output file but none provided.";
  86.     this.MSG_RS_TEMPLATE_NOT_FOUND      = "No render settings template was found with the given name.";
  87.     this.MSG_OM_TEMPLATE_NOT_FOUND      = "No output module template was found with the given name.";
  88.     this.MSG_CAN_NOT_OPEN_SOCKET        = "Can not open socket.";
  89.     this.MSG_NO_COMP_YES_TEMPLATE       = "WARNING: -RStemplate argument ignored since no -comp provided.";
  90.     this.MSG_NO_COMP_YES_OMTEMPLATE     = "WARNING: -OMtemplate argument ignored since no -comp provided.";
  91.     this.MSG_NO_COMP_YES_OUTPUT         = "WARNING: -output argument ignored since no -comp provided.";
  92.     this.MSG_NO_COMP_YES_START_OR_END   = "WARNING: -s and/or -e arguments ignored since no -comp provided.";
  93.     this.MSG_SKIPPING_WILL_CONTINUE     = "INFO: Skipping render queue item with correct comp name but marked to continue from a partly complete render.";
  94.     this.MSG_RENDER_ABORTED            = "INFO: Render aborted.";
  95.     this.MSG_LOG_DIR_NO_EXISTS        = "ERROR: Directory specified for log file does not exist: ";
  96.     this.MSG_LOG_DIR_NOT_A_DIR         = "ERROR: Directory specified for log file is a file, not a directory: ";
  97.     this.MSG_LOG_CAN_NOT_OPEN        = "ERROR: Can not open log file. Try checking write protection of directory: ";
  98.     //
  99.     // Variables for rendering
  100.     //
  101.     this.log_file            = null;
  102.     this.has_user_log_file  = false;
  103.     this.is_verbose_mode   = true;
  104.     this.saved_sound_setting = null;
  105.     this.my_sound_setting    = null;
  106.  
  107.     // [2] define all the functions used by this class
  108.     //
  109.     // Report an error. This writes errors to the log file, if present.
  110.     // This is called from the context of the application, so we
  111.     // need to precede variable names with gAECommandLineRenderer
  112.     // 
  113.     function my_onError(error_string, severity_string)
  114.     {
  115.         // This method is called with a variety of types of messages.
  116.         // The severity_string tells us what kind.
  117.         // Choices are:
  118.         // NAKED, INFO, WARNING, PROBLEM, FATAL, PROGRESS, and DEBUG
  119.  
  120.         // Two of these, PROBLEM and FATAL, are errors that should cause us to change
  121.         // the exit code:
  122.         if (severity_string == "PROBLEM" || severity_string == "FATAL"){
  123.             // These two errors cause us to change the exit code.
  124.             // We don't write an error or throw here, because we got here as part of a thrown 
  125.             // error already, and the message will be printed as part of the catch.
  126.             gAECommandLineRenderer.SetExitCode(gAECommandLineRenderer.EXIT_AE_RUNTIME);
  127.         } else {
  128.             // PROBLEM and FATAL will throw exceptions, and so will be logged to the file
  129.             // when we catch the exception.
  130.             // All other errors (NAKED, INFO, WARNING, PROGRESS, and DEBUG) will not 
  131.             // throw exceptions.  So we log them to the file right here:
  132.             if (gAECommandLineRenderer.is_verbose_mode) {
  133.                 if (gAECommandLineRenderer.log_file != null){
  134.                     if (severity_string == "NAKED"){
  135.                         // everybody is confused by this category.  Just use INFO instead.
  136.                         gAECommandLineRenderer.log_file.writeln("INFO:" + error_string);
  137.                     } else {
  138.                         gAECommandLineRenderer.log_file.writeln(severity_string + ":" + error_string);
  139.                     }
  140.                 }
  141.             }
  142.         }
  143.         // call the error handler that was in place before we started rendering.
  144.         if (gAECommandLineRenderer.oldErrorHandler){
  145.             gAECommandLineRenderer.oldErrorHandler(error_string,severity_string);
  146.         }
  147.     }
  148.  
  149.     // Report an error and throw an exception.
  150.     // Causes the script to exit.
  151.     function my_SetExitCodeAndThrowException(code, message)
  152.     {
  153.         this.SetExitCode(code);
  154.         throw (this.EXIT_MSG_PREFIX[code] + message);
  155.     }
  156.  
  157.     // Report an error. This establishes exitCodes for reporting errors from AfterFX.
  158.     function my_SetExitCode(code)
  159.     {
  160.         // Some codes are set differently depending on whether we have a custom user 
  161.         // log file.  Check for these and use the alternate if appropriate.
  162.         var real_code = code;
  163.         if (gAECommandLineRenderer.has_user_log_file){
  164.             switch(real_code){
  165.             case gAECommandLineRenderer.EXIT_SYNTAX_ERROR:
  166.                 real_code = gAECommandLineRenderer.EXIT_SYNTAX_ERROR_USER_LOG;
  167.                 break;
  168.             case gAECommandLineRenderer.EXIT_OTHER_SCRIPTING_ERROR:
  169.                 real_code = gAECommandLineRenderer.EXIT_OTHER_SCRIPTING_ERROR_USER_LOG;
  170.                 break;
  171.             case gAECommandLineRenderer.EXIT_AERENDER_RUNTIME:
  172.                 real_code = gAECommandLineRenderer.EXIT_AERENDER_RUNTIME_USER_LOG;
  173.                 break;
  174.             case gAECommandLineRenderer.EXIT_AE_RUNTIME:
  175.                 real_code = gAECommandLineRenderer.EXIT_AE_RUNTIME_USER_LOG;
  176.                 break;
  177.             }
  178.         }
  179.  
  180.         // Always keep the first error. So only set if the exitCode is still EXIT_OK.
  181.         if (app.exitCode == gAECommandLineRenderer.EXIT_OK){
  182.             app.exitCode = real_code;
  183.         }
  184.     }
  185.  
  186.     // Arguments may be enclosed in quotes.  This 
  187.     // will remove them and return the result.
  188.     function my_StripAnyEnclosingQuotes(inString)
  189.     {
  190.         var result = inString;
  191.         if (inString && 
  192.             inString.charAt(0) == '"' && 
  193.             inString.charAt(inString.length-1) == '"'){
  194.                 result = inString.substring(1,inString.length-1);
  195.         }
  196.         return result;
  197.     }
  198.  
  199.     // Make sure the value is there, and returns it, stripping away any enclosing quotes.
  200.     //
  201.     function my_GetValueForFlag(arg_num, the_flag)
  202.     {
  203.         if (!this.inArgs[arg_num]){
  204.             this.SetExitCodeAndThrowException(this.EXIT_SYNTAX_ERROR, (this.MSG_UNDEFINED_VALUE_FOR_FLAG + the_flag));
  205.         }
  206.         return this.StripAnyEnclosingQuotes(this.inArgs[arg_num]);
  207.     }
  208.  
  209.     // Parse the parameter.
  210.     // Return the number of arguments used in parsing the parameter.
  211.     function my_ParseParamStartingAt(arg_num)
  212.     {
  213.         if (!this.inArgs[arg_num]){
  214.             this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_TRIED_TO_PARSE_UNDEFINED);
  215.         }
  216.  
  217.         var num_args_parsed = 0;
  218.  
  219.         // Check for a valid flag:
  220.         var my_flag = this.inArgs[arg_num];
  221.         if (my_flag == "-port"){
  222.             // -port is used by aerender to specify a port address for a socket.
  223.             //
  224.             // Note: this value is sought/parsed earlier, in the SetupDefaultLog method.
  225.             // We can just ignore here.
  226.             var dummy  = this.GetValueForFlag(arg_num+1,my_flag);
  227.             num_args_parsed = 2;
  228.         }
  229.         if (my_flag == "-project"){
  230.             this.in_project_path   = this.GetValueForFlag(arg_num+1,my_flag);
  231.             num_args_parsed = 2;
  232.         }
  233.         if (my_flag == "-comp") {
  234.             this.in_comp_name   = this.GetValueForFlag(arg_num+1,my_flag);
  235.             num_args_parsed = 2;
  236.         } 
  237.         if (my_flag == "-RStemplate") {
  238.             this.in_RStemplate   = this.GetValueForFlag(arg_num+1,my_flag);
  239.             num_args_parsed = 2;
  240.         } 
  241.         if (my_flag == "-OMtemplate") {
  242.             this.in_OMtemplate   = this.GetValueForFlag(arg_num+1,my_flag);
  243.             num_args_parsed = 2;
  244.         } 
  245.         if (my_flag == "-output") {
  246.             this.in_output_path = this.GetValueForFlag(arg_num+1,my_flag);
  247.             num_args_parsed = 2;
  248.         }
  249.         if (my_flag == "-log") {
  250.             this.in_logfile_path = this.GetValueForFlag(arg_num+1,my_flag);
  251.             num_args_parsed = 2;
  252.         }
  253.         if (my_flag == "-s") {
  254.             this.in_start_frame = this.GetValueForFlag(arg_num+1,my_flag);
  255.             num_args_parsed = 2;
  256.         }
  257.         if (my_flag == "-e") {
  258.             this.in_end_frame = this.GetValueForFlag(arg_num+1,my_flag);
  259.             num_args_parsed = 2;
  260.         }
  261.         if (my_flag == "-mem_usage") {
  262.             this.in_image_cache_percent   = this.GetValueForFlag(arg_num+1,my_flag);
  263.             this.in_max_mem_percent       = this.GetValueForFlag(arg_num+2,my_flag);
  264.             num_args_parsed = 3;
  265.         }
  266.         if (my_flag == "-v") {
  267.             this.in_verbose_flag = this.GetValueForFlag(arg_num+1,my_flag);
  268.             num_args_parsed = 2;
  269.         }
  270.         if (my_flag == "-close") {
  271.             this.in_close_flag = this.GetValueForFlag(arg_num+1,my_flag);
  272.             num_args_parsed = 2;
  273.         }
  274.         if (my_flag == "-sound") {
  275.             this.in_sound_flag = this.GetValueForFlag(arg_num+1,my_flag);
  276.             num_args_parsed = 2;
  277.         }
  278.         if (my_flag == "-doSavePrefsOnQuit") {
  279.             // The effect of this flag will be taken into account when we
  280.             // exit the app. See comment in the "finally" block.
  281.             // All we do here is increment the num_args_parsed count.
  282.             num_args_parsed = 1;
  283.         }
  284.  
  285.         if (num_args_parsed == 0) {
  286.             this.SetExitCodeAndThrowException(this.EXIT_SYNTAX_ERROR, (this.MSG_BAD_FLAG + my_flag));
  287.         }
  288.  
  289.         return num_args_parsed;
  290.     }
  291.  
  292.     // This parses the inArgs array.  Assumes
  293.     // the array has already been filled.
  294.     function my_ParseInArgs()
  295.     {
  296.         // First, undefine all the inputs we're potentially looking for
  297.         this.in_project_path   = null;
  298.         this.in_comp_name      = null;
  299.         this.in_RStemplate     = null;
  300.         this.in_OMtemplate     = null;
  301.         this.in_output_path    = null;
  302.         this.in_logfile_path   = null;
  303.         this.in_start_frame    = null;
  304.         this.in_end_frame      = null;
  305.         this.in_image_cache_percent   = null;
  306.         this.in_max_mem_percent       = null;
  307.         this.in_verbose_flag   = null;
  308.         this.in_close_flag   = null;
  309.         this.in_sound_flag   = null;
  310.  
  311.         // Special case: check if any argument is "-help"
  312.         for (var i = 0; i < this.inArgs.length; i++){
  313.             if (this.inArgs[i] == "-help"){
  314.                 this.SetExitCodeAndThrowException(this.EXIT_SHOW_USAGE, this.MSG_SHOW_USAGE);
  315.             }
  316.         }
  317.  
  318.         var arg_num = 0; 
  319.         while( arg_num < this.inArgs.length ){
  320.             // ParseParamStartingAt returns the number of arguments used up parsing the param.
  321.             arg_num += this.ParseParamStartingAt(arg_num);
  322.         }
  323.     }
  324.  
  325.     // This arg is treated differently than others because it's extra important
  326.     // that we exit properly in the face of anything that might go wrong even
  327.     // during initialization. So we don't parse the standard way, we check this
  328.     // before exit...
  329.     function my_IsSavePrefsArgGiven(arglist)
  330.     {
  331.         return this.IsInArray("-doSavePrefsOnQuit", arglist);
  332.     }
  333.  
  334.     // Returns true if the item equals an item in the array, false if otherwise.
  335.     function my_IsInArray(needle, haystack)
  336.     {
  337.         result = false;
  338.         for (var i = 0; i < haystack.length; i++){
  339.             if (needle == haystack[i]){
  340.                 result = true;
  341.                 break;
  342.             }
  343.         }
  344.         return result;
  345.     }
  346.  
  347.     function my_SetupDefaultLog(arg_list)
  348.     {
  349.         this.has_user_log_file = false;
  350.  
  351.         // Clean up after a potentially bad exit last time:
  352.         if (this.log_file && this.log_file != null){
  353.             this.log_file.close();
  354.             this.log_file = null;
  355.         }
  356.  
  357.         // Open the socket.
  358.         // It is used:
  359.         // [a] to log errors if there is no user-specified log file (specified with a "-log" arg)
  360.         // [b] to log errors encountered while opening any user-specified log file.
  361.  
  362.         // See if a -port argument was passed:
  363.         this.log_file = null;
  364.         for (var i = 0; i < arg_list.length; i++){
  365.             if (arg_list[i] == "-port"){
  366.                 if (arg_list.length > i+1){
  367.                     // The argument value is the port address
  368.                     this.in_port_address = arg_list[i+1];
  369.                     // Yes, the log_file variable is being used to hold a socket.
  370.                     this.log_file = new Socket();
  371.                     if ( !this.log_file.open(this.in_port_address,"binary") ) {
  372.                         this.log_file = null;
  373.                         this.SetExitCodeAndThrowException(this.EXIT_CANNOT_OPEN_SOCKET, 
  374.                                                         this.MSG_CAN_NOT_OPEN_SOCKET);
  375.                     }
  376.                 }
  377.             }
  378.         }
  379.         this.is_verbose_mode   = true;
  380.     }
  381.  
  382.     function my_CleanupDefaultLog()
  383.     {
  384.         // Close the log file
  385.         if (this.log_file != null){
  386.             this.log_file.close();
  387.             this.log_file = null;
  388.         }
  389.     }
  390.  
  391.     // This is the external entry point.
  392.     // Bat files or executables may call this method.
  393.     //
  394.     // This function assumes that it has been passed all the arguments.
  395.     // It parses the arguments and then renders.
  396.     function my_Render() 
  397.     {
  398.         app.beginSuppressDialogs();
  399.         try {
  400.             this.SetupDefaultLog(my_Render.arguments);
  401.  
  402.             // start by assuming successful execution, exit code 0.
  403.             app.exitCode = 0;
  404.  
  405.             // Check number of arguments
  406.             if (!my_Render.arguments || my_Render.arguments.length == 0){
  407.                 this.SetExitCodeAndThrowException(this.EXIT_SHOW_USAGE, this.MSG_SHOW_USAGE);
  408.             } 
  409.  
  410.             var numArgs = my_Render.arguments.length ;
  411.             // Allocate the array of arguments:
  412.             this.inArgs = new Array(numArgs);
  413.  
  414.             // Load the input arguments into the inArgs array.
  415.             for (var i = 0; i < numArgs; i++) {
  416.                 this.inArgs[i] = my_Render.arguments[i];
  417.             }
  418.  
  419.             // Parse the arguments, and render
  420.             this.ParseInArgs();
  421.             this.ReallyRender();
  422.  
  423.         } catch(error) {
  424.             // Add any errors to the log file.
  425.             if (this.log_file != null){
  426.                 this.log_file.writeln(error.toString());
  427.             }
  428.             this.SetExitCode(this.EXIT_AE_RUNTIME);
  429.         } 
  430.  
  431.         finally {
  432.             // This arg is treated differently than others because it's extra important
  433.             // that we exit properly in the face of anything that might go wrong even
  434.             // during initialization. So we don't parse the standard way, we check this
  435.             // before exit...
  436.             app.setSavePreferencesOnQuit(this.IsSavePrefsArgGiven(my_Render.arguments));
  437.  
  438.             this.CleanupDefaultLog();
  439.             app.endSuppressDialogs(false);
  440.         }
  441.     }
  442.  
  443.     function my_ReallyRender()
  444.     {
  445.         this.saved_sound_setting = null;
  446.     
  447.         try {
  448.             // While rendering we'll report errors to the log file.
  449.             if (app.onError == this.onError){
  450.                 // If the previous error handler is just this one, don't store it. 
  451.                 // That can happen in extreme cases where this script does not get a
  452.                 // chance to clean up and put back the oldErrorHandler when it's done.
  453.                 this.oldErrorHandler = null;
  454.             } else {
  455.                 this.oldErrorHandler = app.onError;
  456.             }
  457.             app.onError = this.onError;
  458.             // Open the user log file, if specified, and use it instead of the socket.
  459.             if (this.in_logfile_path) {
  460.                 // Keep the socket open; errors we encounter while opening the
  461.                 // user log file will be logged to the socket.
  462.                 var user_log_file = new File(this.in_logfile_path);
  463.                 var parent_dir = user_log_file.parent;
  464.                 if (!parent_dir.exists){
  465.                     if (this.log_file){
  466.                         this.log_file.writeln(this.MSG_LOG_DIR_NO_EXISTS + this.in_logfile_path);
  467.                     }
  468.                     this.SetExitCodeAndThrowException(this.EXIT_AE_RUNTIME, this.MSG_AE_RUNTIME);
  469.                 }
  470.                 var test_folder = Folder(parent_dir);
  471.                 if (!(test_folder instanceof Folder)){
  472.                     if (this.log_file){
  473.                         this.log_file.writeln(this.MSG_LOG_DIR_NOT_A_DIR + this.in_logfile_path);
  474.                     }
  475.                     this.SetExitCodeAndThrowException(this.EXIT_AE_RUNTIME, this.MSG_AE_RUNTIME);
  476.                 }
  477.                 if (!user_log_file.open("w",'TEXT','ttxt')){
  478.                     if (this.log_file){
  479.                         this.log_file.writeln(this.MSG_LOG_CAN_NOT_OPEN + this.in_logfile_path);
  480.                     }
  481.                     this.SetExitCodeAndThrowException(this.EXIT_AE_RUNTIME, this.MSG_AE_RUNTIME);
  482.                 }
  483.  
  484.                 // no errors were encountered opening the file.
  485.                 // Close the socket and use this one instead.
  486.                 if (this.log_file != null){
  487.                     this.log_file.close();
  488.                 }
  489.                 this.log_file = user_log_file; // which is still open
  490.                 this.has_user_log_file = true;
  491.             }
  492.  
  493.             if (this.in_verbose_flag){
  494.                 if (this.in_verbose_flag == "ERRORS"){
  495.                     this.is_verbose_mode   = false;    
  496.                 } else if (this.in_verbose_flag == "ERRORS_AND_PROGRESS") {
  497.                     this.is_verbose_mode   = true;
  498.                 } else {
  499.                     this.SetExitCodeAndThrowException(this.EXIT_SYNTAX_ERROR, this.MSG_BAD_VERBOSE_FLAG);
  500.                 }
  501.             }
  502.             if (this.in_close_flag){
  503.                 if (this.in_close_flag != "DO_NOT_CLOSE" &&
  504.                     this.in_close_flag != "DO_NOT_SAVE_CHANGES" &&
  505.                     this.in_close_flag != "SAVE_CHANGES"){
  506.                     this.SetExitCodeAndThrowException(this.EXIT_SYNTAX_ERROR, this.MSG_BAD_CLOSE_FLAG);
  507.                 }
  508.             }
  509.             if (this.in_sound_flag){
  510.                 if (this.in_sound_flag != "ON" &&
  511.                     this.in_sound_flag != "OFF" &&
  512.                     this.in_sound_flag != "on" &&
  513.                     this.in_sound_flag != "off"){
  514.                     this.SetExitCodeAndThrowException(this.EXIT_SYNTAX_ERROR, this.MSG_BAD_SOUND_FLAG);
  515.                 }
  516.             }
  517.  
  518.             // Set the memory usage, if specified as an argument.
  519.             if (this.in_image_cache_percent && this.in_max_mem_percent){
  520.                 app.setMemoryUsageLimits(this.in_image_cache_percent, this.in_max_mem_percent);
  521.             }
  522.  
  523.             // If the user provided a project, close the current project and open the project specified.
  524.             // Else, leave the current project open.
  525.             if (this.in_project_path){
  526.                 // Close the current project
  527.                 if (app.project != null){
  528.                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
  529.                 }            
  530.                 
  531.                 // Open the specified project:
  532.                 var proj_file = new File(this.in_project_path);
  533.                 app.open(proj_file);
  534.             }
  535.             if (!app.project){
  536.                 this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_NO_PROJECT);
  537.             }
  538.  
  539.             // Get the RenderQueueItem for the specified comp, if specified.
  540.             var rqi = null;
  541.             if (this.in_comp_name){
  542.                 rqi = this.GetFirstQueueableRQItemWithName(this.in_comp_name);
  543.             }
  544.             if (this.in_comp_name && !rqi) {
  545.                 // Try to find the comp in the project and add to the render queue:
  546.                 rqi = this.AddCompToRenderQueue(this.in_comp_name);
  547.                 if (rqi){
  548.                     if (this.log_file != null){
  549.                         this.log_file.writeln(this.MSG_ADDING_TO_RQ);
  550.                     }
  551.                 } else {
  552.                     this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_COMP_NOT_FOUND);
  553.                 }
  554.             }
  555.  
  556.             // Apply the templates, if provided
  557.             if (this.in_RStemplate){
  558.                 if (!this.in_comp_name){
  559.                     if (this.log_file != null){
  560.                         this.log_file.writeln(this.MSG_NO_COMP_YES_TEMPLATE);
  561.                     }
  562.                 } else {
  563.                     if (!this.IsInArray(this.in_RStemplate, rqi.templates)) {
  564.                         this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_RS_TEMPLATE_NOT_FOUND);
  565.                     }
  566.                     rqi.applyTemplate(this.in_RStemplate);
  567.                 }
  568.             }
  569.             if (this.in_OMtemplate){
  570.                 if (!this.in_comp_name){
  571.                     if (this.log_file != null){
  572.                         this.log_file.writeln(this.MSG_NO_COMP_YES_OMTEMPLATE);
  573.                     }
  574.                 } else {
  575.                     if (!this.IsInArray(this.in_OMtemplate, rqi.outputModule(1).templates)) {
  576.                         this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_OM_TEMPLATE_NOT_FOUND);
  577.                     }
  578.                     rqi.outputModule(1).applyTemplate(this.in_OMtemplate);
  579.                 }
  580.             }
  581.  
  582.             // If a comp was specified, make it the only one to render.
  583.             // If none was provided, leave everything alone so render queue renders as is.
  584.             if (this.in_comp_name) {
  585.                 this.EstablishAsOnlyQueuedItem(rqi);
  586.             }
  587.  
  588.             if (this.in_comp_name){
  589.                 // If the user provided a path, set the output path on rqi's OutputModule
  590.                 if (rqi.status == RQItemStatus.NEEDS_OUTPUT && !this.in_output_path){
  591.                     this.SetExitCodeAndThrowException(this.EXIT_AERENDER_RUNTIME, this.MSG_NEEDS_OUTPUT);
  592.                 }
  593.                 if (this.in_output_path){
  594.                     var om = rqi.outputModule(1);
  595.                     om.file = new File(this.in_output_path);
  596.                 }
  597.             } else {
  598.                 if (this.in_output_path){
  599.                     if (this.log_file != null){
  600.                         this.log_file.writeln(this.MSG_NO_COMP_YES_OUTPUT);
  601.                     }
  602.                 }
  603.             }
  604.  
  605.             // Set the start and end frames.
  606.             if (this.in_start_frame || this.in_end_frame) {
  607.                 if (!this.in_comp_name){
  608.                     if (this.log_file != null){
  609.                         this.log_file.writeln(this.MSG_NO_COMP_YES_START_OR_END);
  610.                     }
  611.                 }
  612.                 else {
  613.                     // Render times are stored as timeSpanStart and timeSpanDuration.
  614.                     // Setting only the timeSpanStart will not change the timeSpanDuration and 
  615.                     // so will move the end time, but we want the end time unchanged if 
  616.                     // it was not specified.
  617.                     // So we must calculate both start_time and end_time, 
  618.                     // then set both timeSpanStart and timeSpanDuration.
  619.                     // Note: frameDuration is stored in the comp.
  620.                     var start_time = rqi.timeSpanStart;
  621.                     var end_time   = rqi.timeSpanStart + rqi.timeSpanDuration;
  622.                     if (this.in_start_frame){
  623.                         start_time = this.in_start_frame * rqi.comp.frameDuration;
  624.                     }
  625.                     if (this.in_end_frame){
  626.                         // The way AE works, final frame is not included.
  627.                         // But aerender wants final frame included.
  628.                         // So, just add 1 to end frame right here before setting
  629.                         // duration for AE:
  630.                         // Note: must call parseInt() here, or the 1 will be added as if it
  631.                         // were a string. For example, 35 would become 351, not 36. Hoo boy!
  632.                         var end_frame_plus_one = parseInt(this.in_end_frame,10) + 1.0;
  633.                         end_time = end_frame_plus_one * rqi.comp.frameDuration;
  634.                     }
  635.                     rqi.timeSpanStart = start_time;
  636.                     rqi.timeSpanDuration = end_time - start_time;
  637.                 }
  638.             }
  639.  
  640.             // If we are in verbose mode, set the log type to ERRORS_AND_PER_FRAME_INFO 
  641.             // for all RQ items we are about to render.
  642.             if (this.is_verbose_mode){
  643.                 this.SetLogPerFrameInfoInRQ();
  644.             }
  645.  
  646.             this.SaveAndSetRenderSoundSetting();
  647.  
  648.             // Render!
  649.             app.project.renderQueue.render();
  650.             
  651.         } catch(error) {
  652.             // Add any errors to the log file.
  653.             if (this.log_file != null){
  654.                 this.log_file.writeln(error.toString());
  655.             }
  656.             
  657.             // It's possible that errors were encountered while trying to render.
  658.             // Stop the render if in progress for a clean exit from the application.
  659.             if (app.project && app.project.renderQueue && app.project.renderQueue.rendering){
  660.                 // This will prevent the message "render stopped by user" from showing up...
  661.                 app.onError = null;
  662.                 app.project.renderQueue.stopRendering();
  663.                 // This will print a better message:
  664.                 if (this.log_file != null){
  665.                     this.log_file.writeln(this.MSG_RENDER_ABORTED);
  666.                 }
  667.                 app.onError = this.onError;
  668.             }
  669.             
  670.             this.SetExitCode(this.EXIT_AE_RUNTIME);
  671.         } 
  672.  
  673.         finally {
  674.         
  675.             // Close the project.
  676.             this.CloseProjectIfDesired();
  677.  
  678.             // Put back the old error handler
  679.             app.onError = this.oldErrorHandler;
  680.  
  681.             // Restore the setting for hearing the render-done sound.
  682.             this.RestoreRenderSoundSetting()
  683.         }
  684.     }
  685.  
  686.     // Returns the first item on the render queue that:
  687.     // [a] contains a comp named comp_name
  688.     // [b] has a render status of QUEUED or UNQUEUED or NEEDS_OUTPUT
  689.     //     Note that if the status is NEEDS_OUTPUT, one better be provided or
  690.     //     an error will result.
  691.     //
  692.     // If not found, returns null.
  693.     //
  694.     function my_GetFirstQueueableRQItemWithName(comp_name)
  695.     {
  696.         var result = null;
  697.  
  698.         var rq = app.project.renderQueue;
  699.         if (rq &&  rq.numItems > 0){
  700.             var cur_item;
  701.             // the items are indexed from 1 to numItems.
  702.             for (var i = 1; i <= rq.numItems; i++){
  703.                 cur_item = rq.item(i);
  704.                 if (cur_item.comp.name == comp_name && 
  705.                     cur_item.status == RQItemStatus.WILL_CONTINUE){
  706.                     if (this.log_file != null){
  707.                         this.log_file.writeln(this.MSG_SKIPPING_WILL_CONTINUE);
  708.                     }
  709.                 }
  710.                 if (cur_item.comp.name == comp_name && 
  711.                     (cur_item.status == RQItemStatus.QUEUED ||
  712.                      cur_item.status == RQItemStatus.UNQUEUED ||
  713.                      cur_item.status == RQItemStatus.NEEDS_OUTPUT)){
  714.                     // We found it!
  715.                     result = cur_item;
  716.                     break;
  717.                 }
  718.             }
  719.         }
  720.  
  721.         return result;
  722.     }
  723.  
  724.     // Find a comp with the given name, and adds it to the render queue.
  725.     // Returns the newly added render queue item
  726.     //
  727.     // If not found, returns null.
  728.     //
  729.     function my_AddCompToRenderQueue(comp_name)
  730.     {
  731.         var result = null;
  732.  
  733.         // Get the comp with the name we are after
  734.         var cur_item;
  735.         var desired_comp = null;
  736.         // the items in the project are indexed from 1 to numItems
  737.         for (var i = 1; i <= app.project.numItems; i++){
  738.             cur_item = app.project.item(i);
  739.             if (cur_item instanceof CompItem && cur_item.name == comp_name){
  740.                 desired_comp = cur_item;
  741.                 break;
  742.             }
  743.         }
  744.  
  745.         // Add the desired_comp to the render queue.  The add() method
  746.         // returns the new render queue item.
  747.         if (desired_comp){
  748.             result = app.project.renderQueue.items.add(desired_comp);
  749.         }
  750.  
  751.         return result;
  752.     }
  753.  
  754.     // Sets the render flag on all RenderQueueItems other than rqi to false,
  755.     //
  756.     function my_EstablishAsOnlyQueuedItem(rqi)
  757.     {
  758.         var rq = app.project.renderQueue;
  759.         if (rq &&  rq.numItems > 0){
  760.             var cur_item;
  761.             // the items are indexed from 1 to numItems.
  762.             for (var i = 1; i <= rq.numItems; i++){
  763.                 cur_item = rq.item(i);
  764.                 if (cur_item == rqi) {
  765.                     cur_item.render = true;
  766.                 } else {
  767.                     // You can only change the render flag when these are the current status value:
  768.                     if (cur_item.status == RQItemStatus.QUEUED || 
  769.                         cur_item.status == RQItemStatus.UNQUEUED || 
  770.                         cur_item.status == RQItemStatus.NEEDS_OUTPUT || 
  771.                         cur_item.status == RQItemStatus.WILL_CONTINUE){
  772.                         cur_item.render = false;
  773.                     }
  774.                 }
  775.             }
  776.         }
  777.     }
  778.  
  779.     // Sets the log type to be ERRORS_AND_PER_FRAME_INFO for all items that are going to render.
  780.     //
  781.     function my_SetLogPerFrameInfoInRQ()
  782.     {
  783.         var rq = app.project.renderQueue;
  784.         if (rq &&  rq.numItems > 0){
  785.             var cur_item;
  786.             // the items are indexed from 1 to numItems.
  787.             for (var i = 1; i <= rq.numItems; i++){
  788.                 cur_item = rq.item(i);
  789.                 if (cur_item.render == true){
  790.                     if (cur_item.status != RQItemStatus.USER_STOPPED &&
  791.                         cur_item.status != RQItemStatus.ERR_STOPPED &&
  792.                         cur_item.status != RQItemStatus.RENDERING &&
  793.                         cur_item.status != RQItemStatus.DONE) {
  794.                         cur_item.logType = LogType.ERRORS_AND_PER_FRAME_INFO;
  795.                     }
  796.                 }
  797.             }
  798.         }
  799.     }
  800.  
  801.     // Closes the project if the close flag specifies to do so
  802.     //
  803.     function my_CloseProjectIfDesired()
  804.     {
  805.         if (app.project) {
  806.             // Close the project we just used, if desired
  807.             if (!this.in_close_flag || this.in_close_flag == "DO_NOT_SAVE_CHANGES"){
  808.                 // If no flag provided, this is the default.
  809.                 app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
  810.             } else {
  811.                 if (this.in_close_flag == "SAVE_CHANGES"){
  812.                     app.project.close(CloseOptions.SAVE_CHANGES);
  813.                 }
  814.                 // otherwise, flag is DO_NOT_CLOSE, so we do nothing.
  815.             }
  816.         }
  817.     }
  818.  
  819.     function my_SaveAndSetRenderSoundSetting()
  820.     {
  821.         // Save the current setting for hearing the render-done sound, we'll restore it later.
  822.         if (app.preferences.havePref("Misc Section",
  823.                                       "Play sound when render finishes") ){
  824.             // Get the current value if the pref exists.
  825.             this.saved_sound_setting = app.preferences.getPrefAsLong("Misc Section",
  826.                                           "Play sound when render finishes");
  827.         } else {
  828.             // default is to play the sound, value of 1.
  829.             // Use this if the pref does not yet exist.
  830.             this.saved_sound_setting = 1;
  831.         }
  832.  
  833.         // Set the setting for hearing the render-done sound, based on the input, default is off.
  834.         this.my_sound_setting = 0;  // 0 is off
  835.         if (this.in_sound_flag && (this.in_sound_flag == "ON" || this.in_sound_flag == "on")){
  836.             this.my_sound_setting = 1;  // 1 is on
  837.         } 
  838.  
  839.         app.preferences.savePrefAsLong("Misc Section",
  840.                                       "Play sound when render finishes",
  841.                                       this.my_sound_setting);
  842.     }
  843.  
  844.     function my_RestoreRenderSoundSetting()
  845.     {
  846.         if (this.saved_sound_setting){
  847.             app.preferences.savePrefAsLong("Misc Section",
  848.                                           "Play sound when render finishes",
  849.                                           this.saved_sound_setting);
  850.         }
  851.     }
  852.  
  853.     // [3] assign all the functions to be method-type attributes.
  854.     //
  855.     this.onError                      = my_onError;
  856.     this.SetExitCodeAndThrowException = my_SetExitCodeAndThrowException;
  857.     this.SetExitCode                = my_SetExitCode;
  858.     this.StripAnyEnclosingQuotes    = my_StripAnyEnclosingQuotes;
  859.     this.GetValueForFlag            = my_GetValueForFlag;
  860.     this.ParseParamStartingAt        = my_ParseParamStartingAt;
  861.     this.ParseInArgs                = my_ParseInArgs;
  862.     this.IsInArray                   = my_IsInArray;
  863.     this.SetupDefaultLog            = my_SetupDefaultLog;
  864.     this.CleanupDefaultLog            = my_CleanupDefaultLog;
  865.     this.Render                        = my_Render;
  866.     this.ReallyRender                = my_ReallyRender;
  867.     this.GetFirstQueueableRQItemWithName = my_GetFirstQueueableRQItemWithName;
  868.     this.AddCompToRenderQueue = my_AddCompToRenderQueue;
  869.     this.EstablishAsOnlyQueuedItem      = my_EstablishAsOnlyQueuedItem;
  870.     this.SetLogPerFrameInfoInRQ          = my_SetLogPerFrameInfoInRQ;
  871.     this.CloseProjectIfDesired          = my_CloseProjectIfDesired;
  872.     this.SaveAndSetRenderSoundSetting = my_SaveAndSetRenderSoundSetting;
  873.     this.RestoreRenderSoundSetting    = my_RestoreRenderSoundSetting;
  874.     this.IsSavePrefsArgGiven          = my_IsSavePrefsArgGiven;
  875. }
  876.  
  877. var gAECommandLineRenderer = new AECommandLineRenderer();
  878.  
  879.  
  880.